package ca.radiologydb.upload;

/***
 *  Original Code provided by 2007 391 team.                      
 *  Original Author:  Fan Deng
 *                                                                  
 *  A rewrite of the original example code that actually bloody works
 *  The original example suffered from many issues that have now
 *  been resolved.
 *  
 *  @author Nicholas Liu
 *
 ***/

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
import oracle.sql.*;
import oracle.jdbc.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageOutputStream;

import ca.radiologydb.util.Template;

//this library used because the Apache doesn't work and request doesn't work with multipart data
import com.oreilly.servlet.MultipartRequest;

public class ImageUploader extends HttpServlet {
	private static final long serialVersionUID = 1L;
	public String response_message;
	public static final int REG_IMG_HEIGHT = 700;
	public static final int THUMB_HEIGHT = 100;

	/**
	 * Called by the server to handle a POST request. The method first logs logs into 
	 * the oracle DBMS using a hard-coded login and password. Once the login is
	 * confirmed, the image identified by the file-path is resized twice and all three
	 * versions of the image are then added to the database in one tuple.
	 * 
	 * The images are also associated with a tuple from the radiology_record table, and
	 * to do so the record id of that tuple must be sent as a parameter in the request.
	 * 
	 * @param request	An object containing the request made of the servlet.
	 * @param response	An object containing the response provided by the servlet.
	 * @throws ServletException
	 * @throws IOException
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// change the following parameters to connect to the oracle database
		String username = "njliu";
		String password = "s1llyfox";
		String drivername = "oracle.jdbc.driver.OracleDriver";
		String dbstring = "jdbc:oracle:thin:@gwynne.cs.ualberta.ca:1521:CRS";
		MultipartRequest req = new MultipartRequest(request, "/tmp", 10000000);

		if (req.getParameter("recordid") != null) {
			System.out.println(req.getParameter("recordid"));
		}
		int record_id = Integer.parseInt(req.getParameter("recordid"));

		try {
			File s = new File("temp.jsp");
			for (Enumeration<String> e = req.getFileNames(); e.hasMoreElements();) {
				s = req.getFile(e.nextElement());
			}

			BufferedImage fullSize = ImageIO.read(s);
			BufferedImage thumbNail = scaleToHeight(fullSize, THUMB_HEIGHT);
			BufferedImage regularSize;

			if (fullSize.getHeight() > REG_IMG_HEIGHT)
				regularSize = scaleToHeight(fullSize, REG_IMG_HEIGHT);
			else
				regularSize = scaleUniform(fullSize, 1);

			// Connect to the database and create a statement
			Connection conn = getConnected(drivername, dbstring, username,
					password);
			Statement stmt = conn.createStatement();

			// Insert an empty blob into the table first. Note that you have to
			// use the Oracle specific function empty_blob() to create an empty
			// blob - pic_id_seq.nextval is a sequence that is in the DB.
			stmt.execute("INSERT INTO pacs_images VALUES(" + record_id + ","
					+ "pic_id_seq.nextval"
					+ ",empty_blob(), empty_blob(), empty_blob())");

			// to retrieve the lob_locator
			// Note that you must use "FOR UPDATE" in the select statement
			String cmd = "SELECT * FROM pacs_images WHERE record_id = "
					+ record_id + " FOR UPDATE";
			ResultSet rset = stmt.executeQuery(cmd);
			rset.next();
			BLOB thumbBlob = ((OracleResultSet) rset).getBLOB(3);
			BLOB regBlob = ((OracleResultSet) rset).getBLOB(4);
			BLOB fullBlob = ((OracleResultSet) rset).getBLOB(5);

			// Write the image to the blob object
			OutputStream outstream1 = thumbBlob.getBinaryOutputStream();
			ImageIO.write(thumbNail, "jpg", outstream1);

			OutputStream outstream2 = regBlob.getBinaryOutputStream();
			ImageIO.write(regularSize, "jpg", outstream2);

			OutputStream outstream3 = fullBlob.getBinaryOutputStream();
			ImageIO.write(fullSize, "jpg", outstream3);

			outstream1.close();
			outstream2.close();
			outstream3.close();

			stmt.executeUpdate("commit");

			/*
			 * Example code to download the image locally into the /tmp/ directory.
			 */
			cmd = "SELECT thumbnail FROM pacs_images WHERE record_id = " + record_id;
			rset = stmt.executeQuery(cmd);
			
			rset.next();

			InputStream input = rset.getBinaryStream(1);
			
			BufferedImage bf = ImageIO.read(input);
			ImageIO.write(bf, "jpg", new FileImageOutputStream(new File("/tmp/sqlimg.jpg")));

			input.close();

			response_message = " Upload OK!  ";
			conn.close();

		} catch (Exception ex) {
			// System.out.println( ex.getMessage());
			response_message = ex.getMessage();
		}
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		Template template = new Template(this.getServletContext().getRealPath("/").toString());
		out.println(template.getTemplateTop());
		// Output response to the client
		
		
		out.println(response_message);
		
		out.println(template.getTemplateBottom());
	}

	/**
	 * Connects the servlet to the database and returns the connection object.
	 * 
	 * @param drivername	The driver name of the DBMS.
	 * @param dbstring		The address of the DB server.
	 * @param username		User name to access the DBMS.
	 * @param password		Password needed to access the DBMS.
	 * @return				A connection object needed for database operations.
	 * @throws Exception
	 */
	private static Connection getConnected(String drivername, String dbstring,
			String username, String password) throws Exception {
		Class drvClass = Class.forName(drivername);
		DriverManager.registerDriver((Driver) drvClass.newInstance());
		return (DriverManager.getConnection(dbstring, username, password));
	}

	/**
	 * Takes a BufferedImage and returns a copy of it scaled by a factor of 1/n.
	 * 
	 * @param image 	The image to be scaled.
	 * @param n			The scaling factor.
	 * @return			The scaled image.
	 */
	public static BufferedImage scaleUniform(BufferedImage image, int n) {

		int w = image.getWidth() / n;
		int h = image.getHeight() / n;

		BufferedImage shrunkImage = new BufferedImage(w, h, image.getType());

		for (int y = 0; y < h; ++y)
			for (int x = 0; x < w; ++x)
				shrunkImage.setRGB(x, y, image.getRGB(x * n, y * n));

		return shrunkImage;
	}

	/**
	 * Scales a BufferedImage to a height n.
	 * 
	 * @param image	The image to be scaled.
	 * @param n		The height to which the image will be scaled.
	 * @return		The scaled image.
	 */
	public static BufferedImage scaleToHeight(BufferedImage image, int n) {

		int w = (image.getWidth() * n) / image.getHeight();
		int h = n;

		BufferedImage scaledImage = new BufferedImage(w, h, image.getType());

		for (int y = 0; y < h; ++y)
			for (int x = 0; x < w; ++x)
				scaledImage.setRGB(
						x,
						y,
						image.getRGB(x * (image.getWidth() / w),
								y * (image.getWidth() / w)));

		return scaledImage;
	}

}
